/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// TGUI - Texus's Graphical User Interface
// Copyright (C) 2012 Bruno Van de Velde (VDV_B@hotmail.com)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
//    you must not claim that you wrote the original software.
//    If you use this software in a product, an acknowledgment
//    in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
//    and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


#ifndef _TGUI_TEXT_BOX_INCLUDED_
#define _TGUI_TEXT_BOX_INCLUDED_

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace tgui
{
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    struct TGUI_API TextBox : public OBJECT, OBJECT_BORDERS, OBJECT_ANIMATION
    {
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// Default constructor
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TextBox();


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// Copy constructor
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TextBox(const TextBox& copy);


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// Desturctor
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ~TextBox();


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// Overload of assignment operator
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TextBox& operator= (const TextBox& right);


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// Creates the text box.
        /// The width and height parameters constain the size before scaling. The textSize parameter changes the text size.
        /// The ScrollbarPathname is the pathname needed to load the scrollbar. If not provided then there will be no possibility
        /// to scroll, thus no text will be added when the text box is full.
        ///
        /// The text box may resize a little bit. It might be a little bigger or smaller than the size passed to this function.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        bool load(unsigned int width, unsigned int height, unsigned int textSize, const std::string scrollbarPathname = "");


        //////////////////////////////////////////////////////////////////////////////////////////////////////
        /// Changes the size of the text box. Note that this function will undo all scaling, as it just
        /// calculates the correct scale factors for you. It is an alternative to setScale.
        /// Note that the text box has to be loaded correctly before calling this function.
        //////////////////////////////////////////////////////////////////////////////////////////////////////
        void setSize(float width, float height);


        //////////////////////////////////////////////////////////////////////////////////////////////////////
        /// Returns the size of the text box, unaffected by scaling.
        //////////////////////////////////////////////////////////////////////////////////////////////////////
        Vector2u getSize() const;


        //////////////////////////////////////////////////////////////////////////////////////////////////////
        /// Returns the size of the text box, after the scaling transformation.
        //////////////////////////////////////////////////////////////////////////////////////////////////////
        Vector2f getScaledSize() const;


        //////////////////////////////////////////////////////////////////////////////////////////////////////
        /// Returns the pathname that was used to load the scrollbar.
        /// When no scrollbar was loaded then this function will return an empty string.
        //////////////////////////////////////////////////////////////////////////////////////////////////////
        std::string getLoadedScrollbarPathname();


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// Changes the text of the text box.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setText(const std::string text);


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// Returns the text of the text box.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        std::string getText();


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// Changes the font of the text. When you don't call this function then the global font will be use.
        /// This global font can be changed by calling the setGlobalFont function from Panel.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setTextFont(const sf::Font& font);


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// Returns the font of the text.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        const sf::Font& getTextFont();


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// Changes the character size of the text.
        /// The minimum text size is 8.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setTextSize(const unsigned int size);


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// Returns the character size of the text.
        /// It is possible that this function returns 0, which means that the text is auto-scaled.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        unsigned int getTextSize();


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// This function will change the character limit. Set NewMaxChars to 0 to disable the limit.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setMaximumCharacters(const unsigned int maxChars = 0);


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// Returns the character limit. Will return 0 when there is no limit.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        unsigned int getMaximumCharacters();


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// Changes the border width and border height of the text box.
        /// Note that these borders are scaled together with the text box!
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setBorders(unsigned int leftBorder   = 0,
                        unsigned int topBorder    = 0,
                        unsigned int rightBorder  = 0,
                        unsigned int bottomBorder = 0);


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// Changes the colors from the text box.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void changeColors(const sf::Color& backgroundColor                      = sf::Color( 50,  50,  50),
                          const sf::Color& textColor                            = sf::Color(  0,   0,   0),
                          const sf::Color& selectedTextColor                    = sf::Color(255, 255, 255),
                          const sf::Color& selectedTextBackgroundColor          = sf::Color( 10, 110, 255),
                          const sf::Color& unfocusedSelectedTextBackgroundColor = sf::Color(110, 110, 255),
                          const sf::Color& borderColor                          = sf::Color(  0,   0,   0));

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// Set the background color that will be used inside the text box.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setBackgroundColor(const sf::Color& backgroundColor);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// Set the text color that will be used inside the text box.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setTextColor(const sf::Color& textColor);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// Set the text color of the selected text that will be used inside the text box.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setSelectedTextColor(const sf::Color& selectedTextColor);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// Set the background color of the selected text that will be used inside the text box.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setSelectedTextBackgroundColor(const sf::Color& selectedTextBackgroundColor);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// Set the background color of the selected text that will be used inside the text box when the text box is not focused.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setUnfocusedSelectedTextBackgroundColor(const sf::Color& unfocusedSelectedTextBackgroundColor);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// Set the border color that will be used inside the text box.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setBorderColor(const sf::Color& borderColor);


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// Get the background color that is currently being used inside the text box.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        const sf::Color& getBackgroundColor();

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// Get the text color that is currently being used inside the text box.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        const sf::Color& getTextColor();

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// Get the text color of the selected text that is currently being used inside the text box.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        const sf::Color& getSelectedTextColor();

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// Get the background color of the selected text that is currently being used inside the text box.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        const sf::Color& getSelectedTextBackgroundColor();

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// Get the background color of the selected that is currently being used inside the text box when the text box is not focused.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        const sf::Color& getUnfocusedSelectedTextBackgroundColor();

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// Get the border color that is currently being used inside the text box.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        const sf::Color& getBorderColor();


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// This function will set the selection point somewhere, so that when you type something the changes will happen there.
        /// Normally you will not need this function.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setSelectionPointPosition(unsigned int charactersBeforeSelectionPoint);


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// Changes the scrollbar of the text box.
        /// Only needed when this wasn't done when creating the text box or if the scrollbar should be changed.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        bool setScrollbar(const std::string scrollbarPathname);


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// Removes the scrollbar from the text box (if there is one). When there are too many lines to fit in the text box then
        /// some lines will be removed.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void removeScrollbar();


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// This will change the width of the selection point.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setSelectionPointWidth(const unsigned int width = 2);


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // These functions are used to receive callback from the EventManager.
        // You normally don't need them, but you can use them to simulate an event.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        bool mouseOnObject(float x, float y);
        void leftMousePressed(float x, float y);
        void leftMouseReleased(float x, float y);
        void mouseMoved(float x, float y);
        void keyPressed(sf::Keyboard::Key Key);
        void textEntered(char Key);
        void mouseNotOnObject();
        void mouseNoLongerDown();


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    private:


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // This function will search where the selection point should be. It will not change the selection point.
        // It will return after which character the selection point should be.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        unsigned int findSelectionPointPosition(float posX, float posY);


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // This function is called when you are selecting text.
        // It will find out which part of the text is selected.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void selectText(float posX, float posY);


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // To keep the drawing as fast as possible, all the calculation are done in front by this function.
        // It is called when the text changes, when scrolling, ...
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void updateDisplayedText();


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // This function is called by updateDisplayedText and will split the text into five pieces so that the text can
        // be easily drawn.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void updateSelectionTexts(float maxLineWidth);


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // When AnimationManager changes the elapsed time then this function is called.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void update();


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Because this struct is derived from sf::Drawable, you can just call the Draw function from your sf::RenderTarget.
        // This function will be called and it will draw the object on the render target.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void draw(sf::RenderTarget& target, sf::RenderStates states) const;


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    public:

        /// The color of the flickering selection pointer
        sf::Color selectionPointColor;

        /// The width in pixels of the flickering selection pointer
        unsigned int selectionPointWidth;


        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    private:

        // The size of the text box
        Vector2u m_Size;

        // Some information about the text
        std::string  m_Text;
        std::string  m_DisplayedText;
        unsigned int m_TextSize;
        unsigned int m_LineHeight;
        unsigned int m_Lines;

        // The maximum characters (0 by default, which means no limit)
        unsigned int m_MaxChars;

        // What is known about the visible lines?
        unsigned int m_TopLine;
        unsigned int m_VisibleLines;

        // Information about the selection
        unsigned int m_SelChars;
        unsigned int m_SelStart;
        unsigned int m_SelEnd;

        // Information about the selection pointer
        sf::Vector2u m_SelectionPointPosition;
        bool m_SelectionPointVisible;

        // Should the text be resplit into the five texts?
        bool m_SelectionTextsNeedUpdate;

        // The colors that are used by the text box
        sf::Color m_BackgroundColor;
        sf::Color m_SelectedTextBgrColor;
        sf::Color m_UnfocusedSelectedTextBgrColor;
        sf::Color m_BorderColor;

        // The sfml Text objects
        sf::Text m_TextBeforeSelection;
        sf::Text m_TextSelection1;
        sf::Text m_TextSelection2;
        sf::Text m_TextAfterSelection1;
        sf::Text m_TextAfterSelection2;

        std::vector<float> m_MultilineSelectionRectWidth;

        // The scrollbar
        Scrollbar* m_Scroll;

        // The pathname used to load the scrollbar (if there is one)
        std::string m_LoadedScrollbarPathname;

        // The render texture, used to draw the text on (this allows to display only a part of the text)
        sf::RenderTexture* m_RenderTexture;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    };

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#endif //_TGUI_TEXT_BOX_INCLUDED_


